﻿using System;
using System.Collections.Generic;
using Loki.Resources;

namespace Loki.Utils
{
    /// <summary>
    /// Loki services base class.
    /// </summary>
    public abstract class LokiService : LokiObject, IService
    {
        /// <summary>
        /// Gets the service name.
        /// </summary>
        /// <value>The service name.</value>
        public abstract string Name { get; }

        /// <summary>
        /// Gets the service identifier.
        /// </summary>
        /// <value>
        /// The service identifier.
        /// </value>
        public abstract Guid ID { get; }

        #region IDisposable
        /// <summary>
        /// Releases all resources used by an instance of the <see cref="LokiService" /> class.
        /// </summary>
        /// <remarks>
        /// This method calls the virtual <see cref="Dispose(bool)" /> method, passing in <strong>true</strong>, and then suppresses 
        /// finalization of the instance.
        /// </remarks>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        ~LokiService()
        {
            Dispose(false);
        }

        /// <summary>
        /// Releases the unmanaged resources used by an instance of the <see cref="LokiService" /> class and optionally releases the managed resources.
        /// </summary>
        /// <param name="P_Disposing">Set to<strong>true</strong> to release both managed and unmanaged resources; <strong>false</strong> to release only unmanaged resources.</param>
        protected virtual void Dispose(bool P_Disposing)
        {
            if (P_Disposing)
            {
                _TypeBuilders.Clear();
            }

            foreach (IDisposable L_Disposable in _Disposables)
            {
                L_Disposable.Dispose();
            }

            _Disposables.Clear();
        }
        #endregion

        #region Object creation
        /// <summary>
        /// Creates a new instance of the specified type.
        /// </summary>
        /// <typeparam name="T">Desired type.</typeparam>
        public T Create<T>()
        {
            Type L_Type = typeof(T);
            if (!_TypeBuilders.ContainsKey(L_Type))
            {
                throw BuildErrorFormat<ArgumentException>(ErrorMessages.Utils_ObjectCreator_NoDefinition, L_Type, this.GetType());
            }
            else
            {
                return (T)_TypeBuilders[L_Type]();
            }
        }

        private Dictionary<Type, Func<object>> _TypeBuilders = new Dictionary<Type, Func<object>>();

        /// <summary>
        /// Adds an new type creation definition.
        /// </summary>
        /// <typeparam name="TInterface">The type of the interface.</typeparam>
        /// <typeparam name="TConcrete">The type of the concrete class.</typeparam>
        protected void DefineTypeCreation<TInterface, TConcrete>()
            where TConcrete : TInterface
        {
            Func<TConcrete> L_RawConstructor = ExpressionHelper.New<TConcrete>().Compile();
            _TypeBuilders[typeof(TInterface)] = () => L_RawConstructor();
        }
        #endregion

        #region Disposable handling
        protected void RegisterDisposable(IDisposable P_Disposable)
        {
            _Disposables.Add(P_Disposable);
        }

        private HashSet<IDisposable> _Disposables = new HashSet<IDisposable>();
        #endregion
    }
}
